package com.pf.selection.utils;

import com.pf.common.enums.ResultRespEnum;
import com.pf.common.exception.BusinessException;
import com.pf.selection.annotation.SelectionFieldEcho;
import com.pf.selection.annotation.SelectionFieldSort;
import com.pf.selection.annotation.convert.Converter;
import com.pf.selection.annotation.convert.DefaultConverter;
import com.pf.selection.annotation.sort.DefaultSelectionEchoSort;
import com.pf.selection.annotation.sort.SelectionEchoSort;
import com.pf.selection.dto.SelectionDTO;
import com.pf.selection.enums.SelectionEchoReturnType;
import com.pf.selection.enums.SelectionSortEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Lists;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.text.Collator;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author PanFei
 * @version 1.0.0
 * @createTime 2023/3/30 20:19
 */
@Slf4j
public class SelectionEchoUtils<T> {

    private static Map<Class<? extends SelectionEchoSort>, SelectionEchoSort> selectionEchoSortMap = new HashMap<>();

    private Class<T> clazz;

    public SelectionEchoUtils(Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * 获取数据下拉回显信息
     *
     * @param dataList 业务数据集合
     * @return
     * @throws Exception
     */
    public Map<String, Object> getSelectionMap(List<T> dataList) throws Exception {
        return getSelectionMap(dataList, null);
    }

    /**
     * 获取数据下拉回显信息
     *
     * @param dataList    业务数据集合
     * @param dataDictMap 数据字典信息
     * @return
     * @throws Exception
     */
    public Map<String, Object> getSelectionMap(List<T> dataList, Map<String, Map<String, String>> dataDictMap) throws Exception {
        // 获取含有@SelectionFieldEcho注解的字段集合
        Map<Class<? extends Annotation>, List<Field>> annotationFieldMap = getRecursiveFieldsByUpward(clazz, Lists.newArrayList(SelectionFieldEcho.class));
        List<Field> fields = annotationFieldMap.get(SelectionFieldEcho.class);

        Map<String, List<SelectionDTO>> fieldSelectionMap = new HashMap<>();
        Map<String, Boolean> fieldEmptyMap = new HashMap<>();
        Map<String, SelectionEchoReturnType> fieldReturnTypeMap = new HashMap<>();
        // 1.初始化字段下拉回显值
        initFieldSelection(fields, fieldSelectionMap, fieldEmptyMap, fieldReturnTypeMap);

        // 2.处理数据
        for (int i = 0; i < dataList.size(); i++) {
            T obj = dataList.get(i);
            for (Field field : fields) {
                // 获取并设置下拉字段回显值信息
                setFieldSelection(obj, field, fieldSelectionMap, fieldEmptyMap, dataDictMap);
                if (i == dataList.size() - 1) {
                    // 对下拉回显字段进行排序，并设置（空白）选项
                    sortFieldSelection(field, fieldSelectionMap, fieldEmptyMap);
                }
            }
        }
        // 3.处理下拉字段选项值
        Map<String, Object> targetFieldSelectionMap = new HashMap<>();
        fieldSelectionMap.forEach((fieldName, selectionList) -> {
            SelectionEchoReturnType selectionEchoReturnType = fieldReturnTypeMap.get(fieldName);
            switch (selectionEchoReturnType) {
                case CODE_LABEL:
                    targetFieldSelectionMap.put(fieldName, selectionList);
                    break;
                case LABEL:
                    List<String> labelList = selectionList.stream().map(SelectionDTO::getLabel).collect(Collectors.toList());
                    targetFieldSelectionMap.put(fieldName, labelList);
                    break;
                default:
                    throw new BusinessException(ResultRespEnum.SYSTEM_ERROR.getCode(), "@SelectionFieldEcho注解 returnType 属性类型不正确");
            }
        });

        return targetFieldSelectionMap;
    }

    /**
     * 下拉字段回显信息排序
     *
     * @param field             下拉字段
     * @param fieldSelectionMap 下拉字段回显信息
     * @param fieldEmptyMap     下拉字段空值信息
     */
    private static void sortFieldSelection(Field field, Map<String, List<SelectionDTO>> fieldSelectionMap, Map<String, Boolean> fieldEmptyMap) throws Exception {
        SelectionFieldEcho selectionFieldEcho = field.getAnnotation(SelectionFieldEcho.class);
        String fieldName = selectionFieldEcho != null && StringUtils.isNotBlank(selectionFieldEcho.alias()) ? selectionFieldEcho.alias() : field.getName();
        SelectionFieldSort selectionFieldSort = field.getAnnotation(SelectionFieldSort.class);
        SelectionSortEnum selectionSortEnum = selectionFieldSort != null && selectionFieldSort.sortBy() != null ? selectionFieldSort.sortBy() : SelectionSortEnum.CHINESE_SORT;
        List<SelectionDTO> selectionList = fieldSelectionMap.get(fieldName);
        // 排序处理
        List<SelectionDTO> sortedSelectionList = Lists.newArrayList();
        switch (selectionSortEnum) {
            case ENGLISH_SORT:
                sortedSelectionList = selectionList.stream().sorted(Comparator.comparing(SelectionDTO::getLabel)).distinct().collect(Collectors.toList());
                break;
            case CHINESE_SORT:
                sortedSelectionList = selectionList.stream().sorted(Comparator.comparing(SelectionDTO::getLabel, Collator.getInstance(Locale.CHINESE))).distinct().collect(Collectors.toList());
                break;
            case FIELD_VALUE_SORT:
                String[] specifyFieldValueSort = selectionFieldSort.fieldValueSort();
                List<String> sortFieldValues = Lists.newArrayList(specifyFieldValueSort);
                sortedSelectionList = selectionList.stream().sorted(sort(SelectionDTO::getLabel, sortFieldValues)).distinct().collect(Collectors.toList());
                break;
            case CUSTOM_SORT:
                Class<? extends SelectionEchoSort> selectionEchoSortClazz = selectionFieldSort.customSort();
                if (DefaultSelectionEchoSort.class.equals(selectionEchoSortClazz)) {
                    throw new BusinessException(ResultRespEnum.SYSTEM_ERROR.getCode(), "@SelectionFieldSort注解未配置customSort属性");
                }
                SelectionEchoSort selectionEchoSort = selectionEchoSortMap.get(selectionEchoSortClazz);
                if (selectionEchoSort == null) {
                    selectionEchoSort = selectionEchoSortClazz.newInstance();
                    selectionEchoSortMap.put(selectionEchoSortClazz, selectionEchoSort);
                }
                Comparator<SelectionDTO> sort = selectionEchoSort.sort();
                sortedSelectionList = selectionList.stream().sorted(sort).distinct().collect(Collectors.toList());
                break;
            default:
                log.error(">>>>> sortFieldSelection：注解【@SelectionFieldSort】配置的sortBy值不正确！", selectionSortEnum);
        }

        // 空值处理
        if (fieldEmptyMap.get(fieldName)) {
            SelectionDTO emptySelection = new SelectionDTO("", "（空白）");
            sortedSelectionList.add(emptySelection);
        }
        fieldSelectionMap.put(fieldName, sortedSelectionList);
    }

    /**
     * 设置下拉字段回显值信息
     *
     * @param obj               每行数据对象
     * @param field             下拉字段
     * @param fieldSelectionMap 下拉字段回显集合
     * @param fieldEmptyMap     下拉字段空值标识集合
     * @param dataDictMap       数据字典
     * @throws Exception
     */
    private static void setFieldSelection(Object obj, Field field, Map<String, List<SelectionDTO>> fieldSelectionMap, Map<String, Boolean> fieldEmptyMap, Map<String, Map<String, String>> dataDictMap) throws Exception {
        // 获取字段名称，如果配置alias，则使用alias；未配置，则获取field的name
        SelectionFieldEcho selectionFieldEcho = field.getAnnotation(SelectionFieldEcho.class);
        String fieldName = selectionFieldEcho != null && StringUtils.isNotBlank(selectionFieldEcho.alias()) ? selectionFieldEcho.alias() : field.getName();
        // 判断某字段是否存在空值，有，则设置该字段空值标识值为true
        field.setAccessible(true);
        Object value = field.get(obj);
        Boolean emptyFlag = fieldEmptyMap.get(fieldName);
        if (emptyFlag || value == null) {
            fieldEmptyMap.put(fieldName, true);
        }

        List<SelectionDTO> selectionList = fieldSelectionMap.get(fieldName);
        Class<? extends Converter> converterClass = selectionFieldEcho.converter();
        String fieldValue = object2Str(value);
        String fieldDesc;
        SelectionDTO selectionDTO = null;
        if (!DefaultConverter.class.equals(converterClass)) {
            String converterKey = selectionFieldEcho.converterKey();
            if (StringUtils.isBlank(converterKey)) {
                Converter converter = converterClass.newInstance();
                fieldDesc = converter.getDescription(fieldValue);
            } else {
                Constructor<? extends Converter> constructor = converterClass.getConstructor(String.class, Map.class);
                Converter converter = constructor.newInstance(converterKey, dataDictMap);
                fieldDesc = converter.getDescription(fieldValue);
            }
            if (StringUtils.isNotBlank(fieldValue)) {
                selectionDTO = new SelectionDTO(fieldValue, fieldDesc);
            }
        } else {
            if (StringUtils.isNotBlank(fieldValue)) {
                selectionDTO = new SelectionDTO(fieldValue, fieldValue);
            }
        }
        if (selectionDTO != null) {
            selectionList.add(selectionDTO);
        }
        fieldSelectionMap.put(fieldName, selectionList);
    }

    /**
     * 初始化字段下拉回显信息
     *
     * @param fields             含有 @SelectionFieldEcho 注解的字段信息
     * @param fieldSelectionMap  字段下拉回显结果信息
     * @param fieldEmptyMap      字段是否存在空值集合
     * @param fieldReturnTypeMap 字段返回类型
     */
    private static void initFieldSelection(List<Field> fields, Map<String, List<SelectionDTO>> fieldSelectionMap, Map<String, Boolean> fieldEmptyMap, Map<String, SelectionEchoReturnType> fieldReturnTypeMap) {
        for (Field field : fields) {
            SelectionFieldEcho selectionFieldEcho = field.getAnnotation(SelectionFieldEcho.class);
            String fieldName = selectionFieldEcho != null && StringUtils.isNotBlank(selectionFieldEcho.alias()) ? selectionFieldEcho.alias() : field.getName();
            fieldSelectionMap.put(fieldName, Lists.newArrayList());
            fieldEmptyMap.put(fieldName, false);
            fieldReturnTypeMap.put(fieldName, selectionFieldEcho.returnType());
        }
    }

    /**
     * @param keyExtractor
     * @param sortFieldValues
     * @param <T>
     * @param <U>
     * @return
     */
    private static <T, U> Comparator<? super T> sort(Function<? super T, ? extends U> keyExtractor, List<String> sortFieldValues) {
        return Comparator.comparing(keyExtractor, (value1, value2) -> {
            if (CollectionUtils.isEmpty(sortFieldValues)) {
                return -1;
            }
            int index1 = sortFieldValues.indexOf(value1);
            int index2 = sortFieldValues.indexOf(value2);
            return index1 - index2;
        });
    }

    /**
     * 将Object类型值转为String
     *
     * @param obj
     * @return
     */
    private static String object2Str(Object obj) {
        if (obj == null) {
            return null;
        }
        return String.valueOf(obj);
    }

    /**
     * 向上级联获取含某注解的字段集合
     *
     * @param clazz               目标实体类型
     * @param annotationClassList 目标注解集合
     * @return 含有目标注解的字段集合
     */
    private static Map<Class<? extends Annotation>, List<Field>> getRecursiveFieldsByUpward(Class<?> clazz, List<Class<? extends Annotation>> annotationClassList) {
        Map<Class<? extends Annotation>, List<Field>> annotationFieldMap = new HashMap<>();
        getRecursiveFieldsByUpward(clazz, annotationClassList, annotationFieldMap);
        return annotationFieldMap;
    }


    /**
     * 向上级联获取含某注解的字段集合
     *
     * @param clazz               目标实体类型
     * @param annotationClassList 目标注解集合
     * @param annotationFieldMap  含有目标注解的字段集合
     */
    private static void getRecursiveFieldsByUpward(Class<?> clazz, List<Class<? extends Annotation>> annotationClassList, Map<Class<? extends Annotation>, List<Field>> annotationFieldMap) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            for (Class annotationClass : annotationClassList) {
                if (field.isAnnotationPresent(annotationClass)) {
                    List<Field> fieldList = annotationFieldMap.get(annotationClass);
                    if (CollectionUtils.isEmpty(fieldList)) {
                        fieldList = Lists.newArrayList();
                    }
                    fieldList.add(field);
                    annotationFieldMap.put(annotationClass, fieldList);
                }
            }
        }

        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null && !superclass.equals(Object.class)) {
            getRecursiveFieldsByUpward(superclass, annotationClassList, annotationFieldMap);
        }
    }
}
